package com.hzmall.task;

import com.hzmall.common.Const;
import com.hzmall.service.impl.OrderService;
import com.hzmall.util.RedisShardedPoolUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * 定时关闭订单任务
 */
@Component
@Slf4j
public class CloseOrderTask {

    @Autowired
    private OrderService orderService;

    @Scheduled(cron = "0 */1 * * * ?")  //每分钟执行一次
    public void closeOrderTaskV1(){
         /**
         * 设置分布式锁，该锁的值为当前时间戳加设定的锁的锁定时间,即过期时间
         */
        Long result = RedisShardedPoolUtil.setNx(Const.REDIS_LOCK.ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+5000));
        if (result !=null && result.intValue() == 1){
            //关闭订单
            log.info("获取到锁，正在进入定时任务..");
            doCloseOrder(Const.REDIS_LOCK.ORDER_TASK_LOCK);
        }else {
            String resultValue = RedisShardedPoolUtil.get(Const.REDIS_LOCK.ORDER_TASK_LOCK); //获取当前分布式锁的值，即锁过期时间,值不为nil时表示有别的进程正在使用，无法获取
            if (resultValue != null && System.currentTimeMillis() > Long.parseLong(resultValue)){ //锁值不为nil并且当前时间已经大于先前设定的时间戳+锁的时间，表明该锁已经过期
                String getSetValue = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+5000));
                /**
                 * getSet方法设置新值，返回旧值，当旧值为nil时，表明当前锁没有被占用，可以获取锁；
                 * 当旧值不为nil，如果当前旧值与之前获取的旧值相同，表明在此期间没有别的进程修改该锁的值，也就代表没有进程占用该锁，该锁可用
                 */
                if (getSetValue == null || (getSetValue != null && StringUtils.equals(getSetValue,resultValue))){
                    //获取到锁，可以关闭订单
                    //关闭订单
                    log.info("获取到锁，正在进入定时任务..");
                    doCloseOrder(Const.REDIS_LOCK.ORDER_TASK_LOCK);
                }else{
                    log.info("没有获取到锁");
                }
            }else{
                log.info("没有获取到锁");
            }
        }
    }

    /**
     * 关闭订单操作
     * @param lockName 获取到的锁的名称
     */

    private void doCloseOrder(String lockName){
        RedisShardedPoolUtil.expire(lockName,5);//获取到锁后，设置锁的过期时间为5秒,防止长时间占用形成死锁
        log.info("进入到任务，设置过期时间5S...");
        Long beginTime = System.currentTimeMillis();
        this.orderService.closeOrder(2);
        Long endTime = System.currentTimeMillis();
        log.info("任务订单关闭完毕，花费时间{}",(endTime-beginTime)/1000);
        RedisShardedPoolUtil.del(lockName);//手动删除锁，防止任务执行完后还不到锁的过期时间
    }
}
